# Copyright (c) 2009 Peter Corbett
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

import random

import gamestate
import utils

dirlist = [(1,1), (1,0), (1,-1), (0,1), (0,-1), (-1,1), (-1,0), (-1,-1)]

class MobAI:
    def __init__(self, pgprl, data, state):
        self.pgprl = pgprl
        self.state = state
        self.data = data
        
    def do_mob_move(self, mob):
        if "sleep" in mob.statuses:
            mob.time += mob.movtime
            return
        pc = self.state.pc
        afraid = "fear" in mob.statuses
        confused = "confused" in mob.statuses and random.random() < 0.75
        can_see_pc = mob.can_see(pc, self.state, self.data)
        #pc_stealthy = "stealth" in pc.statuses or "blind" in mob.statuses
        adjacent = mob.adjacent_to(pc)
        #adjacent = abs(pc.x - mob.x) <= 1 and abs(pc.y - mob.y) <= 1
        if adjacent and afraid:
            if self.try_cast(mob, "Blink"):
                return            
        if mob.hp * 2 < mob.maxhp and not adjacent:
            if self.try_cast(mob, "Cure Light Wounds"):
                return
        if adjacent and not afraid and can_see_pc and not confused:
            self.pgprl.resolve_attack(mob, pc)
            mob.time += mob.atktime
            return
        if can_see_pc and not confused:
            if self.try_cast(mob, "Magic Missile"):
                return
            dir = self.suggest_direction(mob, (pc.x, pc.y), afraid)
            if dir:
                mob.move_to((dir[0] + mob.x, dir[1] + mob.y), self.state, self.data)
                #mob.x, mob.y = dir[0] + mob.x, dir[1] + mob.y
                mob.time += mob.movtime
            return
        if "last_pc_loc" in mob.aidict:
            lpcl = mob.aidict["last_pc_loc"]
            dir = self.suggest_direction(mob, lpcl, afraid)
            if dir:
                mob.move_to((dir[0] + mob.x, dir[1] + mob.y), self.state, self.data)
                #mob.x, mob.y = dir[0] + mob.x, dir[1] + mob.y
                mob.time += mob.movtime
#mob.x, mob.y = dir
                if (mob.x, mob.y) == lpcl:
                    del mob.aidict["last_pc_loc"]
                else:
                    mob.aidict["momentum"] = dir
                return
        if "momentum" in mob.aidict:
            if self.try_move(mob, mob.aidict["momentum"]):
                return
            else:
                left = utils.rotate(mob.aidict["momentum"], -2)
                right = utils.rotate(mob.aidict["momentum"], 2)
                lp = self.state.current_level.passable(mob.x + left[0], mob.y + left[1], self.data, self.state.pc) == True
                rp = self.state.current_level.passable(mob.x + right[0], mob.y + right[1], self.data, self.state.pc) == True
                if lp and not rp:
                    if self.try_move(mob, left):
                        mob.aidict["momentum"] = left
                        return
                elif rp and not lp:
                    if self.try_move(mob, right):
                        mob.aidict["momentum"] = right
                        return
                del mob.aidict["momentum"]
        if not self.state.lostab[mob.x][mob.y] and not confused and "stealth" not in mob.statuses:
            if self.try_cast(mob, "Stealth"):
                return
        if self.try_move(mob, random.choice(dirlist)):
            return

        #If in doubt, idle
        mob.time += mob.movtime
                
    def suggest_direction(self, mob, target, away=False):
        tlist = [(i[0] + mob.x, i[1] + mob.y) for i in dirlist]
        tlist = [i for i in tlist if self.state.current_level.passable(i[0], i[1], self.data, self.state.pc) == True]
        dir = None
        bestdist = 1000
        for i in tlist:
            dx, dy = abs(i[0] - target[0]), abs(i[1] - target[1])
            dist = 2 * max(dx, dy) + min(dx, dy)
            if away:
                dist = -dist
            if dist < bestdist:
                bestdist = dist
                dir = i
        return (dir[0] - mob.x, dir[1] - mob.y) if dir else None

    def try_move(self, mob, direction):
        tx = direction[0] + mob.x
        ty = direction[1] + mob.y
        contents = self.state.current_level.passable(tx, ty, self.data, self.state.pc)
        if contents == True:
            mob.move_to((tx, ty), self.state, self.data)
            #mob.x = tx
            #mob.y = ty
            mob.time += mob.movtime
            return True
        else:
            return False

    def try_cast(self, mob, spellname):
        spellnum = self.data.spelldict[spellname]
        if spellnum in mob.spells:
            stype = self.data.spelltypes[spellnum]
            if mob.mp < stype.mana:
                return False
            mob.mp -= stype.mana
            if self.state.pc.can_see(mob, self.state, self.data): #self.state.lostab[mob.x][mob.y]:
                self.pgprl.add_message("%s casts %s." % (mob.desc(self.data, "the", True), stype.name))
            mob.del_status("stealth", self.data)
            self.pgprl.magic.cast(mob, mob.sdict["magpow"], stype)
            mob.time += mob.casttime
            return True
        return False

